package com.cheezeworld.entity
{
	
	import com.cheezeworld.math.*;
	import com.cheezeworld.screens.Root;
	
	import flash.utils.Dictionary;
	
	public class MovingEntity extends Entity
	{
		private const BOUNCE_STOP_SPEED:Number	 = 50;
		public static const BOUNDS_BOUNCE:String = "bounce";
		public static const BOUNDS_WRAP:String	 = "wrap";
		public static const BOUNDS_REMOVE:String = "remove";
		public static const BOUNDS_NONE:String 	 = "none";
		
		public function get friction() : Number { return _friction; }
		public function set friction( a_value:Number ) : void { _friction = a_value; }
		/** CAUTION!
		 * The heading variable is left open for speed purposes, however it should only be set externally
		 * using the setHeading function! The side variable should never be set externally.
		 */		
		public var heading:Vector2D;
		public var side:Vector2D;
		public function setHeading( a_value:Vector2D ):void
		{
	  		if(a_value.lengthSq==0)
	  		{
	  			heading.x=1;heading.y=0;
	  		} else 
	  		{
	  			heading=a_value;
	  		}
	  		side = heading.getPerp();
	  	}  		
	
		public var speed:Number;
	  	public var maxSpeed:Number;
	  	public var boundsBehavior:String;
	  	public var doesRotMatchHeading:Boolean;
		
		public function MovingEntity( a_params:MovingEntityParams )
		{
			super( a_params );
			
			_velocity = new Vector2D();
	 		heading = new Vector2D( 1, 0 );
	 		side = heading.getPerp();
	 		_forces = [];
	 		_constantForces = new Dictionary();
			speed = 0;
	 		_newVel = new Vector2D();
	 		
	 		doesRotMatchHeading = a_params.doesRotMatchHeading;
	 		maxSpeed = a_params.maxSpeed;
	 		boundsBehavior = a_params.boundsBehavior;
	 		_friction = a_params.friction;
		}
		
		public function rotateToPosition( a_target:Vector2D ) : void
		{
			rotation = Math.atan2( a_target.y - actualPos.y, a_target.x - actualPos.x );
	 	}
		
		public function applyForce( a_force:Vector2D ):void
		{
			_forces.push( a_force );
		}
		
		public function addConstantForce( a_force:Vector2D, a_id:String ) : void
		{
			_constantForces[ a_id ] = a_force;
		}
		
		public function removeForce( a_id:String ) : void
		{
			delete _constantForces[ a_id ];
			_constantForces[ a_id ] = null;
		}
	 	
	 	public override function update( a_timePassed:int ):void
		{
			setInitialValues(a_timePassed);
			
			calculateForces();
			
			calculateFinalVelocity();
			
	 		updatePosition();
	 		
	 		updateHeading();
	 		
	 		updateChildren( a_timePassed );
		}
	 	
	 	override public function dispose():void
	 	{
	 		_forces = null;
	 		_constantForces = null;
	 		_newVel = null;
	 		_velocity = null;
	 		heading = null;
	 		side = null;
	 		super.dispose();
	 	}
	 	
	 	// -- PRIVATE --
	 	
	 	override protected function setInitialValues( a_timePassed:int ):void
	 	{		
	 		_stepSize = a_timePassed * 0.001;
	 		actualPos.x = newPos.x;
			actualPos.y = newPos.y;
	 		
	 		// .. Update bounds ...
	 		bounds.center.x = actualPos.x
			bounds.center.y = actualPos.y;
			bounds.left = bounds.center.x - bounds.halfWidth;
			bounds.right = bounds.center.x + bounds.halfWidth;
			bounds.top = bounds.center.y - bounds.halfHeight;
			bounds.bottom = bounds.center.y + bounds.halfHeight;
			bounds.topLeft.x = bounds.left; 
			bounds.topLeft.y = bounds.top;
			bounds.bottomRight.x = bounds.right; 
			bounds.bottomRight.y = bounds.bottom;
			bounds.topRight.x = bounds.right; 
			bounds.topRight.y = bounds.top;
			bounds.bottomLeft.x = bounds.left; 
			bounds.bottomLeft.y = bounds.bottom;
			
			if( speed > maxSpeed )
			{
				speed = maxSpeed;
			}
			else if( speed < -maxSpeed )
			{
				speed = -maxSpeed;
			}
	 	}
	 	
	 	protected function calculateForces():void
	 	{ 	
	 		var acc:Vector2D = new Vector2D();
					
			var force:Vector2D;
			var i:int = _forces.length;
	 		while( --i >= 0 )
	 		{
				force = _forces[i];
				acc.x += force.x;
				acc.y += force.y;
			}
			
			_forces.splice( 0, _forces.length );
			
			for each( force in _constantForces )
			{
				acc.x += force.x;
				acc.y += force.y;
			}
	 		
	 		_newVel.x += acc.x;
	 		_newVel.y += acc.y;
	 	}
	 	
	 	protected function calculateFinalVelocity():void
	 	{
	 		_velocity.x = _newVel.x;
	 		_velocity.y = _newVel.y;
			
			_newVel.x = _velocity.x;
	 		_newVel.y = _velocity.y;
	 	}
	 			
	 	protected function updatePosition():void
	 	{
	 		var dx:Number = _velocity.x * _stepSize;
	 		var dy:Number = _velocity.y * _stepSize;	 			
	 		
			actualPos.x += dx;
			actualPos.y += dy;
	 		
	 		switch( boundsBehavior )
	 		{
	 			case BOUNDS_WRAP:
	 				wrapOnBounds();
	 			break;
	 			case BOUNDS_BOUNCE:
	 				bounceOnBounds();
	 			break;
	 			case BOUNDS_REMOVE:
	 				removeOnBounds();
	 			break;
	 		}
	 		
	 		newPos.x = actualPos.x;
	 		newPos.y = actualPos.y;
	 	}
	 	
	 	protected function updateHeading(  ):void
	 	{
	 		if( ( _velocity.x * _velocity.x + _velocity.y * _velocity.y ) > 0.001 )
	 		{
	 			heading = _velocity.getNormalized();
	 			side.x = -heading.y;
				side.y = heading.x;
	 			
	 			if( doesRotMatchHeading )
	 			{
	 				var x:Number = heading.x;
					var y:Number = heading.y;
		 			rotation = Math.atan( y / x );
	 			}
					
	 		} 
	 		else 
	 		{
	 			_velocity.x=0;
	 			_velocity.y=0;
	 		}
	 	}
	 	
	 	protected function wrapOnBounds():void
	 	{
	 		if( actualPos.x > _parent.bounds.width + radius )
	 		{
	 			actualPos.x = 0-radius + ( actualPos.x - _parent.bounds.width );
	 		}
	 		else if( actualPos.y > _parent.bounds.height+radius )
	 		{
	 			actualPos.y = 0-radius + ( actualPos.y - _parent.bounds.height );
	 		}
	 		else if( actualPos.x < -radius )
	 		{
	 			actualPos.x = _parent.bounds.width+radius + actualPos.x;
	 		}
	 		else if( actualPos.y < -radius )
	 		{
	 			actualPos.y = _parent.bounds.height+radius + actualPos.y;
	 		}
	 	}
	 	
	 	protected function bounceOnBounds():void
	 	{
	 		
	 		if( actualPos.x > _parent.bounds.width - radius )
	 		{
	 			_newVel.x *= -1;
	 			_newVel.x *= _friction;
	 			_newVel.y *= _friction;			
	 			if( _newVel.x <= BOUNCE_STOP_SPEED && _newVel.x >= -BOUNCE_STOP_SPEED ){ _newVel.x = 0; }
	 			if( _newVel.y <= BOUNCE_STOP_SPEED && _newVel.y >= -BOUNCE_STOP_SPEED ){ _newVel.y = 0; }
	 			actualPos.x = _parent.bounds.width - radius;
	 		}
	 		else if( actualPos.x < radius )
	 		{
	 			_newVel.x *= -1;
	 			_newVel.x *= _friction;	 	
	 			_newVel.y *= _friction;	 			
	 			if( _newVel.x <= BOUNCE_STOP_SPEED && _newVel.x >= -BOUNCE_STOP_SPEED ){ _newVel.x = 0; }
	 			if( _newVel.y <= BOUNCE_STOP_SPEED && _newVel.y >= -BOUNCE_STOP_SPEED ){ _newVel.y = 0; }
	 			actualPos.x = radius;
	 		}	 		
	 		
	 		if( actualPos.y > _parent.bounds.height - radius )
	 		{
	 			_newVel.y *= -1;
	 			_newVel.x *= _friction;	
	 			_newVel.y *= _friction;
	 			if( _newVel.x <= BOUNCE_STOP_SPEED && _newVel.x >= -BOUNCE_STOP_SPEED ){ _newVel.x = 0; }
	 			if( _newVel.y <= BOUNCE_STOP_SPEED && _newVel.y >= -BOUNCE_STOP_SPEED ){ _newVel.y = 0; }
	 			actualPos.y = _parent.bounds.height - radius;
	 		}
	 		
	 		else if( actualPos.y < radius )
	 		{
	 			_newVel.y *= -1;
	 			_newVel.x *= _friction;	
	 			_newVel.y *= _friction;
	 			if( _newVel.x <= BOUNCE_STOP_SPEED && _newVel.x >= -BOUNCE_STOP_SPEED ){ _newVel.x = 0; }
	 			if( _newVel.y <= BOUNCE_STOP_SPEED && _newVel.y >= -BOUNCE_STOP_SPEED ){ _newVel.y = 0; }
	 			actualPos.y = radius;
	 		}	 		
	 	}
	 	
	 	protected function removeOnBounds():void
	 	{
	 		if( actualPos.x > _parent.bounds.width + radius )
	 		{
	 			_parent.removeChild(id);
	 		}
	 		else if( actualPos.y > _parent.bounds.height+radius )
	 		{
	 			_parent.removeChild(id);
	 		}
	 		else if( actualPos.x < -radius )
	 		{
	 			_parent.removeChild(id);
	 		}
	 		else if( actualPos.y < -radius )
	 		{
	 			_parent.removeChild(id);
	 		}
	 		
	 	}
	 	
	 	protected var _forces:Array;
	 	protected var _constantForces:Dictionary;
	 	protected var _newVel:Vector2D;
	 	protected var _velocity:Vector2D;
	 	protected var _friction:Number;
	}
}
 		